package org.example.warehouse.service.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.example.core.exception.ExceptionCode;
import org.example.core.exception.ware.NoLockedStockException;
import org.example.core.to.mq.OrderTo;
import org.example.core.to.mq.StockDetailTo;
import org.example.core.utils.PageUtils;
import org.example.core.utils.R;
import org.example.rabbitmq.service.RabbitService;
import org.example.warehouse.api.OrderService;
import org.example.warehouse.bo.WareSkuBO;
import org.example.warehouse.dao.WareOrderTaskDao;
import org.example.warehouse.dao.WareSkuDao;
import org.example.warehouse.dto.WareSkuDto;
import org.example.warehouse.entity.WareOrderTaskEntity;
import org.example.warehouse.entity.WareSkuEntity;
import org.example.warehouse.vo.LockedStockVo;
import org.example.warehouse.vo.StockItemVo;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.example.warehouse.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;


@Service("wareSkuService")
public class WareSkuServiceImpl implements WareSkuService {

    @Autowired
    private WareSkuDao wareSkuDao;
    @Autowired
    private WareOrderTaskDao wareOrderTaskDao;
    @Autowired
    private RabbitService rabbitService;
    @Autowired
    private OrderService orderService;
    @Autowired
    private RedissonClient redissonClient;

    // 锁定库存
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void lockStock(LockedStockVo vo) {
        for (StockItemVo stockVo : vo.getStockItem()) {
            int rows = wareSkuDao.updateStock(stockVo.getSkuId(), stockVo.getCount());
            // 减库存成功
            if (rows > 0) {
                WareOrderTaskEntity orderTaskEntity = new WareOrderTaskEntity(null, vo.getOrderId(), vo.getOrderSn(), null, stockVo.getSkuId(), stockVo.getCount(), 1);
                wareOrderTaskDao.addWareOrderTask(orderTaskEntity);
                StockDetailTo stockDetailTo = new StockDetailTo();
                BeanUtils.copyProperties(orderTaskEntity, stockDetailTo);
                // 发送消息
                rabbitService.sendMessage("stock-event-exchange", "stock.locked", stockDetailTo);
            } else {
                // 失败
                throw new NoLockedStockException("锁定库存失败");
            }
        }
    }

    // 自动解锁
    @Override
    public void autoUnLockedStock(StockDetailTo to) {
        WareOrderTaskEntity wareOrderTask = wareOrderTaskDao.getWareOrderTaskById(to.getId());
        if (wareOrderTask != null){
            // 解锁
            R<Integer> status = orderService.status(to.getOrderSn());
            if (status.getCode() == 0) {
                if (status.getData() == 5 || status.getData() == null) {
                    // 订单取消或不存在
                    if (wareOrderTask.getLockedStatus() == 1){
                        handleAutoUnlocked(to.getSkuId(), to.getSkuNum(), to.getOrderSn());
                    }
                }
            } else {
                throw new RuntimeException("远程服务调用失败");
            }
        }
    }

    // 主动解锁
    @Override
    public void activeUnLockedStock(OrderTo order) {
        R<Integer> status = orderService.status(order.getOrderSn());
        if (status.getCode() == 0) {
            if (status.getData() == 5 || status.getData() == null) {
                // 订单取消或不存在
                handleActiveUnlocked(order.getOrderSn());
            }
        } else {
            throw new RuntimeException("远程服务调用失败");
        }
    }

    // 添加库存
    @Override
    public int addWareSkus(List<WareSkuEntity> wareSku) {
        return wareSkuDao.addWareSkus(wareSku);
    }

    // 判断是否有库存
    @Override
    public Boolean queryStockBySkuId(Long skuId) {
        Integer stock = wareSkuDao.getStockBySkuId(skuId);
        return stock > 0;
    }

    /**
     * 查询sku集合是否有库存
     * @param skuIds
     * @return
     */
    @Override
    public Map<Long, Integer> querySkusIsStocks(List<Long> skuIds) {
        List<WareSkuEntity> skuEntities = wareSkuDao.getSkusIsStocks(skuIds);
        Map<Long, Integer> collect = skuEntities.stream().collect(Collectors.toMap(WareSkuEntity::getSkuId, WareSkuEntity::getStock));
        return collect;
    }


    /**
     * 查询单个sku库存数
     * @param skuId
     * @return
     */
    @Override
    public Integer queryStock(Long skuId) {
        return wareSkuDao.getStockBySkuId(skuId);
    }

    @Override
    public Map<Long, Integer> getSeckillSkuStock(List<Long> skuIds) {
        List<WareSkuEntity> wareSkuEntities = wareSkuDao.getSeckillSkuStock(skuIds);
        Map<Long, Integer> collect = wareSkuEntities.stream()
                .collect(Collectors.toMap(WareSkuEntity::getSkuId, WareSkuEntity::getStock, (oldValue, newValue) -> newValue));
        return collect;
    }

    @Override
    public PageUtils list(WareSkuDto dto) {
        if (dto.needLimit()){
            PageHelper.startPage(dto.getPage(), dto.getPageSize());
        }
        List<WareSkuEntity> entities = wareSkuDao.list(dto);
        return new PageUtils(new PageInfo<>(entities));
    }

    @Override
    public int update(WareSkuBO bo) {
        return wareSkuDao.update(bo);
    }

    /**
     * 这个加了个 and `stock_locked`-#{num}>=0的条件好像不会存在并发问题，可能因为数据库update本身有锁，但是会多执行一次操作数据库的语句
     * 可用分布式锁解决，这里我没用
     */
    @Transactional(rollbackFor = Exception.class)
    public void handleAutoUnlocked(Long skuId, Integer num, String orderSn) {
        // 解锁
        wareSkuDao.unLockedStock(skuId, num);
        // 修改锁定状态
        wareOrderTaskDao.updateLockedStatus(skuId, 0 , orderSn);
    }

    @Transactional(rollbackFor = Exception.class)
    public void handleActiveUnlocked(String orderSn) {
        // 获取需要解锁的
        List<WareOrderTaskEntity> entities = wareOrderTaskDao.getWareOrderTaskList(orderSn, 1);
        // 解锁
        for (WareOrderTaskEntity entity : entities) {
            wareSkuDao.unLockedStock(entity.getSkuId(), entity.getSkuNum());
            // 修改锁定状态
            wareOrderTaskDao.updateLockedStatus(entity.getSkuId(), 0 , orderSn);
        }
    }
}